<?php
/**
* The MIT License
* Copyright (c) 2006 Johan Nilsson http://johan.galaxen.net/ http://markupartist.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy 
* of this software and associated documentation files (the "Software"), to deal 
* in the Software without restriction, including without limitation the rights 
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
* copies of the Software, and to permit persons to whom the Software is 
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all 
* copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
* SOFTWARE.
*/

require_once 'Base/List/Exception.php';
require_once 'Base/List/Iterator.php';

/**
 * Basic List implementation that implements the SPL interface 
 * IteratorAggregate and Countable.
 * 
 * Example usage: 
 * <code>
 *   $coll = new Base_List();
 *   $coll->attach('a');
 *   $coll->attach('b');
 *   $coll->attach('c');
 *   $coll->attach('d');
 * 
 *   foreach ($coll as $item)
 *   {
 * 	   echo $item . "\n";
 *   }
 * </code>
 * 
 * @author Johan Nilsson
 */
class Base_List implements IteratorAggregate, Countable
{
	protected $_items = array();

	/**
	 * Adds an item to the List.
	 * 
	 * Returns an instance of it self, this Basekes it possible to call 
	 * this method in a chain, for example;
	 * 
	 * <code>
	 *   $coll->attach('a')->attach('b')->attach('c');
	 * </code>
	 * 
	 * @param mixed $obj
	 * @param string $key - Optional
	 * 
	 * @return self
	 */
	public function attach($obj, $key = null)
	{
		if ( !is_null($key) )
		{
			if ( $this->exists($key) )
			{
				throw new Base_List_Exception('Key in use: ' . $key);
			}
			$this->_items[$key] = $obj;
		}
		else
		{
			$this->_items[] = $obj;
		}

		return $this;
	}

	/**
	 * Adds an item to the list, making it the first element, Please note that
	 * all numeric keys will be reset.
	 *
	 * @param mixed $obj
	 * @param string $key - Optional
	 * @return self
	 */
	public function prepend($obj, $key = null)
	{
		if ( !is_null($key) )
		{
			if ( $this->exists($key) )
			{
				throw new Base_List_Exception('Key in use: ' . $key);
			}
			$this->_items = array_merge(array($key => $obj), $this->_items);
		}
		else
		{
			array_unshift($this->_items, $obj);
		}

		return $this;
	}

	/**
	 * Removes an item from the List.
	 * 
	 * Returns an instance of it self, this Basekes it possible to call 
	 * this method in a chain, for example;
	 * 
	 * <code>
	 *   $coll->remove('a')->remove('b')->remove('c');
	 * </code>
	 * 
	 * @param string $key
	 * 
	 * @throws Base_List_Exception
	 * 
	 * @return void
	 */
	public function remove($key)
	{
		if ( !isset($this->_items[$key]) )
		{
			throw new Base_List_Exception('Undefined key: ' . $key);
		}

		unset($this->_items[$key]);

		return $this;
	}

	/**
	 * Returns an item from the List.
	 *
	 * @param string $key
	 * 
	 * @throws Base_List_Exception
	 * 
	 * @return mixed
	 */
	public function get($key)
	{
		if ( !isset($this->_items[$key]) )
		{
			throw new Base_List_Exception('Undefined key: ' . $key);
		}

		return $this->_items[$key];
	}

	/**
	 * Returns the first element of the list.
	 *
	 * @return mixed
	 */
	public function first()
	{
		if ( count($this) > 0 )
		{
			return current(array_slice($this->_items, 0, 1, true));
		}
		return '';
	}

	/**
	 * Returns the last element of the list.
	 *
	 * @return mixed
	 */
	public function last()
	{
		if ( count($this) > 0 )
		{
			return current(array_slice($this->_items, count($this) -1, count($this), true));
		}
		return '';
	}

	/**
	 * Counts and returns the number of items in this List.
	 *
	 * @return int
	 */
	public function count()
	{
		return count($this->_items);
	}

	/**
	 * Checks if an item exists in the List.
	 *
	 * @param string $key
	 * 
	 * @return boolean
	 */
	public function exists($key)
	{
		return isset($this->_items[$key]);
	}

	/**
	 * Returns all keys.
	 *
	 * @return array
	 */
	public function keys()
	{
		return array_keys($this->_items);
	}

	/**
	 * Returns an iterator for this List.
	 *
	 * @return Base_List_Iterator
	 */
	public function getIterator()
	{
		return new Base_List_Iterator(clone $this);
	}
}